All files / web/src/app/api/players/[id] route.ts

57.95% Statements 51/88
15.38% Branches 2/13
100% Functions 0/0
57.95% Lines 51/88

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 891x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x   1x           1x 1x 1x 1x 1x 1x             1x 1x 1x 1x 1x 1x 1x 1x 1x                     1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                               1x  
import { eq } from 'drizzle-orm'
import { NextResponse } from 'next/server'
import { db, schema } from '@/db'
import { withAuth } from '@/lib/auth/withAuth'
import { isParentOf } from '@/lib/classroom/access-control'
import { getUserId } from '@/lib/viewer'
import { normalizeBirthdayInput } from '@/lib/playerAge'
 
/**
 * PATCH /api/players/[id]
 * Update a player (owner or linked parent)
 */
export const PATCH = withAuth(async (request, { params }) => {
  try {
    const { id } = (await params) as { id: string }
    const userId = await getUserId()
    const body = await request.json()
    let normalizedBirthday: string | null | undefined
    if (body.birthday === null) {
      normalizedBirthday = null
    } else if (typeof body.birthday === 'string') {
      normalizedBirthday = normalizeBirthdayInput(body.birthday)
      if (normalizedBirthday === null) {
        return NextResponse.json({ error: 'Invalid birthday' }, { status: 400 })
      }
    }
 
    // Check authorization: owner or linked parent
    const authorized = await isParentOf(userId, id)
    if (!authorized) {
      return NextResponse.json({ error: 'Player not found or unauthorized' }, { status: 404 })
    }

    // Security: Only allow updating specific fields (excludes userId)
    const [updatedPlayer] = await db
      .update(schema.players)
      .set({
        ...(body.name !== undefined && { name: body.name }),
        ...(body.emoji !== undefined && { emoji: body.emoji }),
        ...(body.color !== undefined && { color: body.color }),
        ...(body.isActive !== undefined && { isActive: body.isActive }),
        ...(body.isArchived !== undefined && { isArchived: body.isArchived }),
        ...(body.notes !== undefined && { notes: body.notes }),
        ...(normalizedBirthday !== undefined && { birthday: normalizedBirthday }),
      })
      .where(eq(schema.players.id, id))
      .returning()

    if (!updatedPlayer) {
      return NextResponse.json({ error: 'Player not found' }, { status: 404 })
    }

    return NextResponse.json({ player: updatedPlayer })
  } catch (error) {
    console.error('Failed to update player:', error)
    return NextResponse.json({ error: 'Failed to update player' }, { status: 500 })
  }
})
 
/**
 * DELETE /api/players/[id]
 * Delete a player (owner or linked parent)
 */
export const DELETE = withAuth(async (_request, { params }) => {
  try {
    const { id } = (await params) as { id: string }
    const userId = await getUserId()
 
    const authorized = await isParentOf(userId, id)
    if (!authorized) {
      return NextResponse.json({ error: 'Player not found or unauthorized' }, { status: 404 })
    }

    const [deletedPlayer] = await db
      .delete(schema.players)
      .where(eq(schema.players.id, id))
      .returning()

    if (!deletedPlayer) {
      return NextResponse.json({ error: 'Player not found' }, { status: 404 })
    }

    return NextResponse.json({ success: true, player: deletedPlayer })
  } catch (error) {
    console.error('Failed to delete player:', error)
    return NextResponse.json({ error: 'Failed to delete player' }, { status: 500 })
  }
})